//go:build v5

package queue

import (
	"context"
	"errors"
	"sync/atomic"
	"unsafe"
)

type ConcurrentLinkedQueue[T any] struct {
	head unsafe.Pointer
	tail unsafe.Pointer
}

type node[T any] struct {
	next unsafe.Pointer
	val  T
}

func NewConcurrentLinkedQueue[T any]() *ConcurrentLinkedQueue[T] {
	head := &node[T]{}
	ptr := unsafe.Pointer(head)
	return &ConcurrentLinkedQueue[T]{
		head: ptr,
		tail: ptr,
	}
}

func (c *ConcurrentLinkedQueue[T]) EnQueue(ctx context.Context, data T) error {
	newNode := &node[T]{val: data}
	newNodePtr := unsafe.Pointer(newNode)
	for {
		if ctx.Err() != nil {
			return ctx.Err()
		}
		tailPtr := atomic.LoadPointer(&c.tail)
		tail := (*node[T])(tailPtr)
		tailNextPtr := atomic.LoadPointer(&tail.next)
		tailNext := (*node[T])(tailNextPtr)
		if tailNext != nil {
			continue
		}

		if atomic.CompareAndSwapPointer(&tail.next, tailNextPtr, newNodePtr) {
			// 修改 c.tail
			atomic.CompareAndSwapPointer(&c.tail, tailPtr, newNodePtr)
			return nil
		}
	}
}

func (c *ConcurrentLinkedQueue[T]) DeQueue(ctx context.Context) (T, error) {
	for {
		var t T
		if ctx.Err() != nil {
			return t, ctx.Err()
		}
		headPtr := atomic.LoadPointer(&c.head)
		head := (*node[T])(headPtr)
		tailPtr := atomic.LoadPointer(&c.tail)
		tail := (*node[T])(tailPtr)
		if head == tail {
			var t T
			return t, errors.New("ekit: 队列为空")
		}
		headNextPtr := atomic.LoadPointer(&head.next)
		if atomic.CompareAndSwapPointer(&c.head, headPtr, headNextPtr) {
			headNext := (*node[T])(headNextPtr)
			return headNext.val, nil
		}
	}

	//for {
	//	headPtr := atomic.LoadPointer(&c.head)
	//	head := (*node[T])(headPtr)
	//	headNextPtr := atomic.LoadPointer(&head.next)
	//	headNext := (*node[T])(headNextPtr)
	//	tailPtr := atomic.LoadPointer(&c.tail)
	//	tail := (*node[T])(tailPtr)
	//	if atomic.CompareAndSwapPointer(&head.next, headNextPtr, headNext.next) {
	//		if tail == headNext {
	//			atomic.CompareAndSwapPointer(&c.tail, tailPtr, headPtr)
	//		}
	//
	//	}
	//
	//}
}
